package main;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Hashtable;

import main.constraint.ConstraintAbstract;
import main.constraint.XDF;
import automata.Fsa;
import automata.State;
import automata.StateSet;

/**
 * @author abrao
 */

public class Configuration implements Comparable<Configuration>
{
	private State s;
	private Fsa f;
	private ConstraintAbstract k;

	public Configuration(State s, Fsa f, ConstraintAbstract k)
	{
		this.s = s;
		this.f = f;
		this.k = k;
	}

	public Configuration() {}
		
	public State getState()
	{
		return s;
	}

	public Fsa getFsa()
	{
		return f;
	}

	public Object getKey()
	{
		return k;
	}
	
	
	/**
	 * Checks that arrived at the end of automaton Source 
	 * @return true or false 
	 */
	public boolean isSourceEnd()
	{
		Fsa f = getFsa();
		State sf = getState();
		StateSet setF = f.get_final_states();
		XDF k = ((XDF) getKey());
		int fposition = k.getFsaPosition(f);
		
		if ((fposition == 2) && (setF.contains(sf)))
			return true;
		
		return false;
	}
	
	/**
	 * Checks that arrived at the end of automaton Target 
	 * @return true or false 
	 */
	public boolean isTargetEnd()
	{
		Fsa f = getFsa();
		State sf = getState();
		StateSet setF = f.get_final_states();
		XDF k = (XDF) getKey();
		int fposition = k.getFsaPosition(f);
		
		if ((fposition == 3) && (setF.contains(sf)))
			return true;
		
		return false;
	}
	
	public int getPosition()
	{
		return ((XDF) k).getJ(s.elt);
	}
	
	public String getEquality()
	{
		return ((XDF) k).getEquality(f, s);
	}
	
	public String toString()
	{
		String str="";
		int fsaType;
		fsaType = ((XDF) getKey()).getFsaPosition(getFsa());
		
		switch(fsaType)
		{
			case 1: str += "M.";
					break;
			case 2: str += "T'.";
					break;
			case 3: str += "T\".";
					break;
		}
		
		str += "e"+(getState().elt);
		
		return str;
	}

	public SyntAttributes validateData(SyntAttributes syntAtt, String sb, String posAux, String nodeName,
			                           Hashtable<String,ArrayList<Inters>> htIntersXfd)
	{
		SyntAttributes syntAttAux = new SyntAttributes();
		int dsj, npaths;
		
		if (isSourceEnd())//M =  T' ^ e = Ft'
		{
		//	System.out.println("Fim do Source End");
			syntAtt.setNodeName(nodeName);
			npaths= ((XDF) getKey()).getNPaths();
			syntAtt.setDsSize(npaths);
			dsj = getPosition();
			String igualdade = getEquality();
			
			if(igualdade.equalsIgnoreCase("V"))
				syntAtt.setDs(sb,dsj);
			else
				syntAtt.setDs(posAux,dsj);
			
			for (int i=0;i<npaths;i++)
			{
				if ((i!=dsj)&& syntAtt.getDs().get(i).equals(""))
				    syntAtt.setDs("#",i);
			}				
		}
		else
		{
			//System.out.println("nao eh fim do node");
			syntAtt.setNodeName(nodeName);
			npaths= ((XDF) getKey()).getNPaths();
			syntAtt.setDsSize(npaths);
			
			for (int i=0;i<npaths;i++)
			{
				if (syntAtt.getDs().get(i).equals(""))
				{
					syntAtt.setDs("#",i);
				}	    
			}			
		}	   
		
		if (isTargetEnd())//M =  T" ^ e = Ft"
		{
		   syntAtt.setNodeName(nodeName);
		   String igualdade = getEquality();
		   if(igualdade.equalsIgnoreCase("V"))
		   {
			   syntAtt.setDc(sb);
		   }
		   else
		   {
			   syntAtt.setDc(posAux);
		   }	   
		} 
		else
		{
			if (syntAtt.getDc().equals(""))
				syntAtt.setDc("#");
		} 
		
		Inters intersection1 = new Inters();
		intersection1.setDc(syntAtt.getDc());
	   	intersection1.setDs(syntAtt.getDs());
	    
	   	if (syntAtt.getInters().isEmpty())
	    	syntAtt.addInters(intersection1);
	    else
	    {
	    	syntAttAux.addInters(intersection1);
	        syntAtt.join(syntAttAux,htIntersXfd,posAux);
	    }    
	    
	   	return syntAtt;
	}
	
	public SyntAttributes validateInNode(SyntAttributes syntAtt, SyntAttributes syntAttAnt, 
			                             String posAnt, String posAt, String nodeName, Hashtable<String, Hashtable<ArrayList<String>, String>> htContextXdf, 
			                             Hashtable<String,ArrayList<Inters>> htIntersXfd, Hashtable<String, ArrayList<String>> hterror)
	{						
		Hashtable<ArrayList<String>, String> htXdf = new Hashtable<ArrayList<String>, String>();
		
		int role = roleType(this);
		//String igualdade;
		int dsj;
		//System.out.println("-- Role :"+role);
		//System.out.println("-- Node Name :"+nodeName.toString());
		//System.out.println("-- SyntAtt "+syntAtt.toString());
		//System.out.println("-- SyntAttAnt "+syntAttAnt.toString());
		switch(role)
		{
			case 1: // Estado final para alguma dependencia, num no que nao eh folha, lado direito (eh isso?)
				//System.out.println("case 1");
				syntAtt = validateData(syntAtt, syntAtt.getDc(), posAt, nodeName, htIntersXfd);
				syntAttAnt.join(syntAtt,htIntersXfd,posAnt);
				break;
			
			case 2: // in function validateData
				//System.out.println("case 2");
				syntAtt.setNodeName(nodeName);
				dsj = getPosition();
				syntAtt = validateData(syntAtt,(String)syntAtt.getDs().get(dsj), posAt, nodeName, htIntersXfd);
				syntAttAnt.join(syntAtt,htIntersXfd,posAnt);
				break;
			
			case 3: // automato lado direito, caminho
				 //System.out.println("case 3");
			     if (!syntAtt.getDc().equals("") && !syntAtt.getDs().isEmpty())
			     { 
				    syntAttAnt.join(syntAtt,htIntersXfd,posAnt);
				    syntAtt.setDc("");
				    syntAtt.setDs(new ArrayList<String>());
			     }
			     else{
				    syntAttAnt.join(syntAtt,htIntersXfd,posAnt); 
			     }
				break;
			
			case 4: // automato lado esquerdo, caminho
				//System.out.println("case 4");
			
			    if (!syntAtt.getDc().equals("") && !syntAtt.getDs().isEmpty())
			    { 			   
			      syntAttAnt.join(syntAtt,htIntersXfd,posAnt);
			      syntAtt.setDc("");
			      syntAtt.setDs(new ArrayList<String>());
			    }  
			    else{
				    syntAttAnt.join(syntAtt,htIntersXfd,posAnt); 
			     }			
				
			    //System.out.println("--++ SyntAtt "+syntAtt.toString());
			    //System.out.println("--++ SyntAttAnt "+syntAttAnt.toString());
				break;
				
			case 5: // Context Automaton - Final State
				System.out.println("case 5 - Contexto:"+posAt);
				Boolean erro = false;
				ArrayList<Inters> al =  syntAtt.getInters();
									
				for(Iterator<Inters> j=al.iterator(); j.hasNext();)
				{
					    Inters elInters = (Inters) j.next();
					    if (!htXdf.containsKey(elInters.getDs()))
					    {
				            htXdf.put(elInters.getDs(), elInters.getDc());
					    }
					    else
					    {
					 		String s = (String) htXdf.get(elInters.getDs());
					 		
					 		if (!s.equals(elInters.getDc()))
					 		{
					 			erro = true;		
					 			ArrayList<String> array = hterror.get(k.getNameConstraint());
					 			array.add(elInters.getDs().toString() + " : " + elInters.getDc().toString());					 			
					 			hterror.put(k.getNameConstraint(), array);					 			
					 		    //break;
					 		}    
					    }	    
		       }
				
			   if (erro == false) 
			      syntAttAnt.addC(true);
			   else
				  syntAttAnt.addC(false);
			   
			   htContextXdf.put(posAt, htXdf);	
			   
			   //System.out.println("case 5 - syntAttAnt:"+syntAttAnt.toString());
			   //System.out.println("case 5 - Contexto:"+htContextXdf.toString());
			   //System.out.println("case 5 - htIntersXfd:"+ htIntersXfd.toString());
			   //System.out.println("case 5 - size contexto" + htContextXdf.size());
			   break;
			   
			case 6:
				System.out.println("case 6");
				Boolean element, ver=true;
				
				ArrayList<Boolean> al2 = (ArrayList<Boolean>) syntAttAnt.getC();
				if (!al2.isEmpty())
				{
				   for(Iterator<Boolean> j=al2.iterator(); j.hasNext();)
				   {
				      element = (Boolean) j.next();
				      ver = element && ver;
				   }
				}
				else
				{
					ver = true; 
				}
				
				ArrayList<Boolean> al1 = (ArrayList<Boolean>) syntAtt.getC();
				for(Iterator<Boolean> j=al1.iterator(); j.hasNext();)
				{
				      element = (Boolean) j.next();
				      ver = element && ver;
				}
				
				ArrayList<Boolean> al3 = new ArrayList<Boolean>();
				al3.add(ver);
				syntAttAnt.setC(al3); 
				//System.out.println("Em caso 6, syntattant: "+syntAttAnt.toString());
					
				break;
				
			default:
				//working...
				break;
		}

		return syntAttAnt;
	}
	
	/**
	 * This rule defines which follow from the verification of the final state and current state
	 * @param Configuration c (object)
	 * @return int
	 */
	public int roleType(Configuration c)
	{
		Fsa f = c.getFsa();
		State sf = c.getState();
		StateSet setF = f.get_final_states();
		XDF k = (XDF) c.getKey();
		int fposition = k.getFsaPosition(f);
		
		if (fposition == 3)//T" - right side
		{
			if (setF.contains(sf))
				return 1; 
			else
				return 3;
		}
		
		if (fposition == 2)//T' - left side
		{
			if (setF.contains(sf))
				return 2;
			else
				return 4;
		}
		
		if (fposition == 1)//M - context
		{
			if (setF.contains(sf))
				return 5;
			else
				return 6;
		}
		
		return 0;
	}

	@Override
	public int compareTo(Configuration o)
	{
		int thisPosition = ((XDF) this.getKey()).getFsaPosition(this.getFsa());
		int objPosition = ((XDF) o.getKey()).getFsaPosition(o.getFsa());
		
		//System.out.println("thisposicao="+thisPosition + " -- objPosicao="+objPosition);
		
		return thisPosition < objPosition ? + 1 : (thisPosition > objPosition ? -1 : 0); 
	}
}